﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics.Contracts;

namespace Postnuclear.Collections.Adapters
  {
  /// <summary>
  /// Adopts <see cref="IObservableEnumerable"/> interface to <see cref="IObservableEnumerable{T}"/> one.
  /// </summary>
  public sealed class ObservableEnumerableAdapter
    : IObservableEnumerable<object>
    {
    #region Public

    // Constructors

    /// <summary>
    /// Initializes a new instance of the <see cref="ObservableEnumerableAdapter"/> class.
    /// </summary>
    /// <param name="source">The source collection.</param>
    public ObservableEnumerableAdapter(IObservableEnumerable source)
      {
      Contract.Requires<ArgumentNullException>(source != null);

      Source = new ObservableCollection<object>(source);
      source.CollectionChanged += OnSourceCollectionChanged;
      }

    // Events

    /// <summary>
    /// Occurs when collection is cleared.
    /// List of items that were cleared from collection is passed as arguments value.
    /// </summary>
    public event EventHandler<ValueEventArgs<IEnumerable<object>>> CollectionCleared
      {
      add
        {
        Source.CollectionCleared += value;
        }

      remove
        {
        Source.CollectionCleared -= value;
        }
      }

    /// <summary>
    /// Occurs when new item is added to collection.
    /// Added item is passed as arguments value.
    /// </summary>
    public event EventHandler<ObservableItemEventArgs<object>> ItemAdded
      {
      add
        {
        Source.ItemAdded += value;
        }

      remove
        {
        Source.ItemAdded -= value;
        }
      }

    /// <summary>
    /// Occurs when item is removed from collection.
    /// Removed item is passed as arguments value.
    /// </summary>
    public event EventHandler<ObservableItemEventArgs<object>> ItemRemoved
      {
      add
        {
        Source.ItemRemoved += value;
        }

      remove
        {
        Source.ItemRemoved -= value;
        }
      }

    /// <summary>
    /// Occurs when the collection changes.
    /// </summary>
    public event NotifyCollectionChangedEventHandler CollectionChanged
      {
      add
        {
        Source.CollectionChanged += value;
        }

      remove
        {
        Source.CollectionChanged -= value;
        }
      }

    /// <summary>
    /// Occurs when a property value changes.
    /// </summary>
    public event PropertyChangedEventHandler PropertyChanged
      {
      add
        {
        Source.PropertyChanged += value;
        }

      remove
        {
        Source.PropertyChanged -= value;
        }
      }

    // Methods

    /// <summary>
    /// Returns an enumerator that iterates through the collection.
    /// </summary>
    /// <returns>
    /// An <see cref="IEnumerator{T}"/> object that can be used to iterate through the collection.
    /// </returns>
    public IEnumerator<object> GetEnumerator()
      {
      return Source.GetEnumerator();
      }

    /// <summary>
    /// Returns an enumerator that iterates through a collection.
    /// </summary>
    /// <returns>
    /// An <see cref="IEnumerator"/> object that can be used to iterate through the collection.
    /// </returns>
    IEnumerator IEnumerable.GetEnumerator()
      {
      return GetEnumerator();
      }

    #endregion

    #region Private

    // Methods

    private void OnSourceCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
      {
      switch (e.Action)
        {
        case NotifyCollectionChangedAction.Add:
          Source.Add(e.NewItems);
          break;

        case NotifyCollectionChangedAction.Remove:
          Source.Remove(e.OldItems);
          break;

        case NotifyCollectionChangedAction.Reset:
          Source.Clear();
          break;
        }
      }

    // Fields

    private readonly IObservableCollection<object> Source;

    #endregion
    }
  }